package smat.test


class Discrete_Bayes_wo {
  def discreteBayes(trainPatterns: List[List[Int]], trainTargets: List[Int], testPatterns: List[List[Int]]): List[Int] = {
    val uc = trainTargets.removeDuplicates.sort((x, y) => x < y)
    var uniquePatterns = List[List[Int]]()
    var counts = List[List[Int]]()
    for(i <- trainPatterns.first.indices){
      val data = trainPatterns.indices.map(row => trainPatterns(row)(i))
      val patMat = data.indices.map(row => trainPatterns.first.indices.map(col => data(row)))
      val diff = patMat.indices.map(row => patMat(row).indices.map(col => trainPatterns(row)(col) - patMat(row)(col)))
      val sum = diff.indices.map(col => {var sum = 0; diff.indices.foreach(row => sum += diff(row)(col)); sum})
      val indices = sum.indices.flatMap(index => if(sum(index) == 0) List(index) else List())
      var isEmpty = true
      uniquePatterns.foreach(row => isEmpty = isEmpty && row.isEmpty)
      if(isEmpty){
        uniquePatterns = data.indices.map(index => List(data(index)))
        val filter = indices.map(index => trainTargets(index))
        counts = uc.indices.map(j => List(filter.count(el => el == uc(j))))
      } else{
        val uqMat = data.indices.map(row => uniquePatterns.first.indices.map(col => data(row)))
        var hasEqual = false
        uqMat.indices.foreach(row => uqMat.first.indices.foreach(col => hasEqual = hasEqual || uqMat(row)(col) == uniquePatterns(row)(col)))
        if(hasEqual){
          uniquePatterns = uniquePatterns.indices.map(row => uniquePatterns(row) ++ List(data(row)))
          val filter = indices.map(index => trainTargets(index))
          counts = uc.indices.map(j => counts(j) ++ List(filter.count(el => el == uc(j))))
        }
      }
    }
    val sc = counts.indices.map(col => {var sum = 0; counts.indices.foreach(row => sum += counts(row)(col)); sum})
    val mat = uc.indices.map(row => sc)
    val pX = mat.indices.map(row => mat(row).indices.map(col => counts(row)(col) / mat(row)(col)))
    var uniqueTargets = List[Int]()
    for(col <- pX.first.indices){
      var max = Math.MIN_INT
      var index = 0
      for(row <- pX.indices){
        if(pX(row)(col) > max){
          max = pX(row)(col)
          index = row
        }
      }
      uniqueTargets = uniqueTargets ++ List(index)
    }
    
    return nearestNeighbor(uniquePatterns, uniqueTargets, testPatterns, 1)
  }
  
  def nearestNeighbor(trainPatterns: List[List[Int]], trainTargets: List[Int], testPatterns: List[List[Int]], knn: Int): List[Int] = {
    val uc = trainTargets.removeDuplicates.sort((x, y) => x < y)
    
    if(trainTargets.size < knn){
      throw new IllegalArgumentException("You specified more neighbors than there are points.")
    }
    
    val n = testPatterns.first.size
    val testTargets = (0 to n-1).map(i => 0).toArray
    for(i <- testPatterns.first.indices){
      val vect = testPatterns.indices.map(row => testPatterns(row)(i))
      val mat = vect.indices.map(row => trainTargets.indices.map(col => vect(row)))
      val diff = mat.indices.map(row => mat(row).indices.map(col => trainPatterns(row)(col) - mat(row)(col)))
      val sq = diff.indices.map(row => mat(row).indices.map(col => diff(row)(col) * diff(row)(col)))
      val dist = sq.indices.map(col => {var sum = 0; sq.indices.foreach(row => sum += sq(row)(col)); sum})
      val uq = dist.removeDuplicates.sort((x, y) => x < y)
      val indices = uq.flatMap(element => dist.indices.flatMap(index => if(dist(index) == element) List(index) else List()))
      val filter = indices.map(index => trainTargets(index))
      val hist = uc.map(el => filter.count(cand => cand == el))
      var best = 0
      val max = hist.sort((x, y) => x > y).first
      hist.indices.foreach(index => if(hist(index) == max) best = index)
      testTargets(i) = uc(best)
    }
    return testTargets.toList
  }
}
